/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cz.muni.fi.diplomka.datalayer.daoimpl;

import cz.fi.muni.diplomka.common.enumeration.UnitType;
import cz.muni.fi.diplomka.datalayer.dao.CityUnitsDao;
import cz.muni.fi.diplomka.datalayer.entity.CityUnitsEntity;
import cz.muni.fi.diplomka.datalayer.util.DbConnector;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.neo4j.cypher.javacompat.ExecutionEngine;
import org.neo4j.cypher.javacompat.ExecutionResult;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Transaction;

/**
 *
 * @author Jakub Kutil
 */
public class CityUnitsDaoImpl implements CityUnitsDao {

    @Override
    public void addUnitsInCity(CityUnitsEntity unitsInCityEntity) {
        if (unitsInCityEntity == null) {
            throw new IllegalArgumentException("Units In City Entity can not be null.");
        }

        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();

        try (Transaction t = gds.beginTx()) {

            Map<String, Object> params = new HashMap<>();

            params.put("cityId", unitsInCityEntity.getCityId());
            params.put("unit", unitsInCityEntity.getUnit().toString());
            params.put("unitCount", unitsInCityEntity.getUnitCount());

            String query = "MATCH (City:City {id : {cityId}})"
                    + "CREATE (UnitsInCity: UnitsInCity {unit : {unit}, unitCount : {unitCount}})"
                    + "<-[r:LOCATED]-(City)";
            engine.execute(query, params);
            t.success();
        }
    }

    @Override
    public void editUnitsInCity(CityUnitsEntity unitsInCityEntity) {
        if (unitsInCityEntity == null) {
            throw new IllegalArgumentException("Units In City Entity can not be null.");
        }

        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();

        try (Transaction t = gds.beginTx()) {

            Map<String, Object> params = new HashMap<>();

            params.put("cityId", unitsInCityEntity.getCityId());
            params.put("unit", unitsInCityEntity.getUnit().toString());
            params.put("unitCount", unitsInCityEntity.getUnitCount());

            String query = "MATCH (City:City { id :{cityId}})-[r:LOCATED]->(UnitsInCity:UnitsInCity { unit : {unit}})"
                    + "SET UnitsInCity.unitCount = {unitCount}";
            engine.execute(query, params);
            t.success();
        }
    }

    @Override
    public List<CityUnitsEntity> getAllUnitsInCity(Long cityId) {
        if (cityId == null) {
            throw new IllegalArgumentException("City Id can not be null.");
        }

        List<CityUnitsEntity> cityUnitsList = new ArrayList<>();
        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();


        try (Transaction t = gds.beginTx()) {

            Map<String, Object> params = new HashMap<>();

            params.put("cityId", cityId);

            String query = "MATCH (City:City { id :{cityId}})-[r:LOCATED]->(UnitsInCity:UnitsInCity)"
                    + "RETURN UnitsInCity";
            ExecutionResult result = engine.execute(query, params);
            Iterator<Node> units = result.columnAs("UnitsInCity");
            while (units.hasNext()) {
                Node node = units.next();

                CityUnitsEntity cityUnitsEntity = new CityUnitsEntity();
                cityUnitsEntity.setCityId(cityId);
                cityUnitsEntity.setUnit(UnitType.valueOf(node.getProperty("unit").toString()));
                cityUnitsEntity.setUnitCount(Integer.valueOf(node.getProperty("unitCount").toString()));

                cityUnitsList.add(cityUnitsEntity);
            }
            t.success();
        }
        return cityUnitsList;
    }

    @Override
    public int getSpecificUnitCountInCity(Long cityId, UnitType unitType) {
        if (cityId == null) {
            throw new IllegalArgumentException("City Id can not be null.");
        }

        if (unitType == null) {
            throw new IllegalArgumentException("Unit Type can not be null.");
        }

        CityUnitsEntity cityUnitsEntity = new CityUnitsEntity();

        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();


        try (Transaction t = gds.beginTx()) {

            Map<String, Object> params = new HashMap<>();

            params.put("cityId", cityId);
            params.put("unit", unitType.toString());

            String query = "MATCH (City:City { id :{cityId}})-[r:LOCATED]->(UnitsInCity:UnitsInCity { unit :{unit}})"
                    + "RETURN UnitsInCity";
            ExecutionResult result = engine.execute(query, params);
            Iterator<Node> units = result.columnAs("UnitsInCity");
            while (units.hasNext()) {
                Node node = units.next();

                cityUnitsEntity.setCityId(cityId);
                cityUnitsEntity.setUnit(UnitType.valueOf(node.getProperty("unit").toString()));
                cityUnitsEntity.setUnitCount(Integer.valueOf(node.getProperty("unitCount").toString()));
            }
            t.success();
        }
        return cityUnitsEntity.getUnitCount();
    }
}
